﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

using NLog;

using Sonic.Framework.Site;
using Sonic.Framework.Tools;
using Sonic.Framework.Extensions;
using Enums = Sonic.Framework.Site.Enums;
using Sonic.Framework.Interfaces;
using Sonic.Framework.ProcessorToolDirectives;

namespace Sonic.Framework
{
    /// <summary>
    /// 
    /// TODO LIST:
    /// add CSS processor rules
    ///  - possible uses include: automatically concat in @import's, datestamping or versioning file names 
    /// 
    /// - working preprocessor
    /// - working postprocessor
    /// - stats collection
    /// - error reporting
    /// - license header at top of classes
    /// - fix svn
    /// 
    /// </summary>
    public class SonicProcessor
    {
        private Stopwatch _stopwatch = new Stopwatch();

        public string LoggerName
        {
            set
            {
                NLogSingleton.SetLoggerName(value);
            }
        }

        /// <summary>
        /// An instance of a logger for writing out debug information
        /// </summary>
        public Logger Logger
        {
            get {
                return NLogSingleton.Instance.Logger;
            }
        }

        /// <summary>
        /// A list of site profiles to be processed by sonic
        /// </summary>
        public List<SiteProfile> Sites = new List<SiteProfile>();

        /// <summary>
        /// A list of tools to be available for the processor when processing site profiles
        /// </summary>
        public Dictionary<string, IProcessorTool> ProcessorTools = new Dictionary<string, IProcessorTool>();

        /// <summary>
        /// A list of plugin processor tool directives. These directives will allow for custom actions to be performed pre and post
        /// ProcessorTool execution. They can be used to filter input, or modify output.
        /// </summary>
        public Dictionary<string, IProcessorToolDirective> ProcessorToolDirectives = new Dictionary<string, IProcessorToolDirective>();


        /// <summary>
        /// Process each site as defined in the config XML used to build this processor
        /// </summary>
        public void Process()
        {
            _stopwatch.Start();

            foreach (SiteProfile site in Sites)
            {
                Logger.Info("Processing site: " + site.RootDirectoryInfo.FullName);

                foreach (Sonic.Framework.Site.SiteDirectory dir in site.Directories)
                {
                    Logger.Info("> Processing directory: " + dir.DirectoryInfo.FullName + " ...");

                    // If directory config is invalid output an explaination of why
                    if (dir.Tool == null || !dir.DirectoryInfo.Exists)
                    {
                        if (dir.Tool == null || !dir.Tool.ExecutablePath.Exists)
                        {
                            Logger.Error("Processor tool not set");
                        }

                        if (!dir.DirectoryInfo.Exists)
                        {
                            Logger.Error("Directory not found.");
                        }
                        continue;
                    }
                    if (dir.Tool.Name != "Null" && !dir.Tool.ExecutablePath.Exists)
                    {
                        Logger.Error("Could not find processor tool exe " + dir.Tool.ExecutablePath.FullName);
                        continue;
                    }

                    // Get the files that we are going to process
                    List<FileInfo> directoryFiles = dir.GetMatchingFiles(Enums.ExclusionType.ProcessorTool);

                    // Run plugged in Pre ProcessorToolDirectives
                    foreach (IProcessorToolDirective plugin in dir.PreProcessorDirectives)
                    {
                        Logger.Info(">> Pre " + plugin.Name);
                        plugin.Execute(ref directoryFiles);
                    }

                    // Run the ProcessorTool over a valid directory
                    dir.Tool.Run(directoryFiles);
                    
                    // Run plugged in Post ProcessorToolDirectives
                    foreach (IProcessorToolDirective plugin in dir.PostProcessorDirectives)
                    {
                        Logger.Info(">> Post " + plugin.Name);
                        plugin.Execute(ref directoryFiles);
                    }
                }
            }

            // TODO: Move into thread safe
            _stopwatch.Stop();
            long timeTakenMs = _stopwatch.ElapsedMilliseconds;

            System.Threading.ThreadPool.QueueUserWorkItem(Logger.Info, ("Processed " + Sites.Count + " sites in " + timeTakenMs + " milliseconds"));
        }
    }
}
